#ifndef __RINGBUFFER__
#define __RINGBUFFER__
#include <memory>
#include<stdint.h>
#include <mutex>

template <typename T> class RingBuffer{
public:
    T At(uint64_t pos);
    RingBuffer(uint64_t size,bool ts);
    int Add(T *data,uint64_t len);
    int TakeBack(T *data,uint64_t len);
    int TakeFront(T *data,uint64_t len);
    int CopyBack(T *data,uint64_t len);

    void SetEmpty();
    uint32_t CanReadCount();
    uint32_t CanWriteCount();
    uint32_t Size();
    uint32_t Length();

    ~RingBuffer();

protected:
    bool m_threadsafe;
    T *m_data;
    T *m_data_end;
    uint64_t m_size; // 容量
    uint32_t m_len;  // 数据长度
    T *m_head;  // 数组头
    std::mutex m_mutex;  // 互斥锁
};

template<typename T>
RingBuffer<T>::RingBuffer(uint64_t size,bool ts) :
    m_data(nullptr),
    m_size(0),
    m_head(0)
{
    m_size = size;
    m_data = new T[size];
    m_data_end = &m_data[size - 1];
    m_head = m_data;
    m_len = 0;
    m_threadsafe = ts;
}

template<typename T>
RingBuffer<T>::~RingBuffer(){
    delete[] m_data;
}

template<typename T>
void RingBuffer<T>::SetEmpty(){
    m_len = 0;
}

template<typename T>
int RingBuffer<T>::CopyBack(T *data,uint64_t len){
    if(data == nullptr)
        return -1;
    T *tail = nullptr;
    if(m_len == 0)
        return 0;
    if((m_head + m_len) > m_data_end){
        tail = m_head + m_len - m_size;
    }else{
        tail = m_head + m_len - 1;
    }

    int i = 0;
    for(i = 0;i < len;i++){
        T* tmp = tail;
        if(tail > m_data) {
            tmp--;
        }
        if(tail == m_data) {
            tmp = m_data_end;
        }
        // next is head stop
        if(tmp == m_head) { 
            data[i] =  (*tail);
            break;
        }
        data[i] = (*tail) ;
        tail = tmp;
    }
}

template<typename T>
uint32_t RingBuffer<T>::Size(){
    return m_size;
}

template<typename T>
uint32_t RingBuffer<T>::Length(){
    return m_len;
}

template<typename T>
T RingBuffer<T>::At(uint64_t pos){
    if(pos > (m_len - 1)){
        return T{};
    }
    T* p = m_head + pos;
    if(p > m_data_end){
        p = p - m_size;
    }
    // printf("\r\n at %u: %d\r\n",p,*p);

    return *p;
}

template<typename T>
int RingBuffer<T>::Add(T *data,uint64_t len){
    if(data == nullptr){
        return -1;
    }
    if(m_len == m_size)
        return 0;
    int left = m_size - m_len;
    if(len > left){
        len = left;
    }

    T *tail = nullptr;
    // like 1023 0 1 2 or 0 1 2 3
    if((m_head + m_len) > m_data_end){
        tail = m_head + m_len - m_size;
    }else{
        tail = m_head + m_len;
    }
    printf("head  %u len %u tail %u\r\n",m_head,len,tail);

    int i = 0;
    for(i = 0;i < len;i++){
        T* tmp = tail;
        if(tail < m_data_end) {
            tmp++;
        }
        if(tail == m_data_end) {
            tmp = m_data;
        }
        if(tmp == m_head) { 
            printf("break %u %u\r\n",m_head,tmp);
            i++;
            break;
        }
        (*tail) = data[i];
        tail = tmp;
        // printf("add %u:%d\r\n",tail,data[i]);
    }
    m_len += i;
    return i;
}

/*

*/
template<typename T>
int  RingBuffer<T>::TakeBack(T *data,uint64_t len){
    if(data == nullptr)
        return -1;
    T *tail = nullptr;
    if(m_len == 0)
        return 0;
    
    if((m_head + (m_len - 1)) > m_data_end){
        tail = m_head + (m_len - 1) - m_size;
        printf("m_data_end:%u m_head + m_len:%u m_head: %u  m_len:%ld tail: %u m_size: %ld m_data:%u\r\n",m_data_end,(m_head + m_len),m_head,m_len,tail,m_size,m_data);
    }else{
        tail = m_head + m_len - 1;
    }

    int i = 0;

    for(i = 0;i < len;i++) {
        data[len - 1 - i] = (*tail) ;
        if((tail == m_head)) { 
            i++;
            break;
        }
        if(tail > m_data) {
            tail--;
        }else if(tail == m_data) {
            tail = m_data_end;
        }
        // next is head stop
    }
    m_len -= i;
    return i ;
}

template<typename T>
int RingBuffer<T>::TakeFront(T *data,uint64_t len){
    if(data == nullptr)
        return -1;
    T *tail = nullptr;
    if(m_len == 0)
        return 0;
    if(len > m_len){
        len = m_len;
    }

    int i = 0;
    for(i = 0;i < len;i++) {
        data[i] = (*m_head);
        if(m_head == tail) { 
            std::cout<<"equal"<<std::endl;
            i++;
            break;
        }
        if(m_head < m_data_end) {
            m_head++;
        }
        if(m_head == m_data_end) {
            m_head = m_data;
        }
    }
    m_len -= i;
    return i;
}

template<typename T>
uint32_t RingBuffer<T>::CanReadCount(){
    return m_len;
}

template<typename T>
uint32_t RingBuffer<T>::CanWriteCount(){
    return m_size - m_len;
}
#endif
